#include "string.h"

#include <utility>

namespace {
  base::uint str_len(base::cchar *str) {
    if (!str) return 0;
    base::uint len = 0;
    while (str[len] != '\0') ++len;
    return len;
  }
  bool str_equal(base::cchar *str1, base::uint len1, base::cchar *str2, base::uint len2) {
    if (!str1 || !str2) return false;
    if (len1 == base::cstring::csize) len1 = str_len(str1);
    if (len2 == base::cstring::csize) len2 = str_len(str2);
    if (len1 != len2) return false;
    for (base::uint i = 0; i < len1; ++i)
      if (str1[i] != str2[i]) return false;
    return true;
  }
  int str_cmp(base::cchar *str1, base::uint len1, base::cchar *str2, base::uint len2, bool nocase) {
    if (!str1 && !str2) return 0;
    if (!str1) return 1;
    if (!str2) return -1;
    if (len1 == base::cstring::csize) len1 = str_len(str1);
    if (len2 == base::cstring::csize) len2 = str_len(str2);
    if (len1 != len2) return len1 > len2 ? 1 : -1;
    for (base::uint i = 0; i < len1; ++i)
      if (str1[i] != str2[i]) {
        if (!nocase) return str1[i] > str2[i] ? 1 : -1;
        if (str1[i] >= 'a' && str1[i] <= 'z'
          && str2[i] >= 'A' && str2[i] <= 'Z'
          && str1[i] - str2[i] == 32) continue;
        if (str1[i] >= 'A' && str1[i] <= 'Z'
          && str2[i] >= 'a' && str2[i] <= 'z'
          && str1[i] - str2[i] == -32) continue;
        return str1[i] > str2[i] ? 1 : -1;
      }
    return 0;
  }
}

namespace base {
  const uint cstring::npos = 0;
  const uint cstring::csize = 0;

  cstring::cstring(cchar *src, uint size) {
    data_ = src; size_ = size;
    if (!data_) { size_ = 0; return; }
    if (size_ == csize) size_ = str_len(data_);
  }
  cstring::~cstring() {}

  bool cstring::empty() const { return size_ > 0; }
  uint cstring::size() const { return size_; }
  cchar* cstring::data() const { return data_; }
  string cstring::string() const {
    base::string str(data_, size_);
    return std::move(str);
  }
  char cstring::at(uint pos) const { return data_[pos]; }

  bool cstring::equal(cchar *str, uint size) const { return str_equal(str, size, data_, size_); }
  bool cstring::start_with(cchar *str, uint size) const { return str_equal(str, size, data_, size); }
  bool cstring::end_with(cchar *str, uint size) const {
    if (size == csize) size = str_len(str);
    if (size_ < size) return false;
    return str_equal(str, size, data_+size_-size, size);
  }

  int cstring::compare(cchar *str, uint size) const { return str_cmp(data_, size_, str, size, false); }
  int cstring::compare_nocase(cchar *str, uint size) const { return str_cmp(data_, size_, str, size, true); }

  uint cstring::find(uint pos, const cstring &str) { return find(pos, str.data(), str.size()); }
  uint cstring::rfind(uint pos, const cstring &str) { return rfind(pos, str.data(), str.size()); }
  uint cstring::find(uint pos, cchar *str, uint size) const {
    if (!str) return npos;
    if (size == csize) size = str_len(str);
    if (pos + size > size_) return npos;
    for (uint i = pos; i < size_ - size; ++i)
      if (str_equal(str, size, data_+i, size)) return i;
    return npos;
  }
  uint cstring::rfind(uint pos, cchar *str, uint size) const {
    if (!str) return npos;
    if (size == csize) size = str_len(str);
    if (pos + size > size_) pos = size_ - size;
    for (uint i = pos; i >= 0; --i)
      if (str_equal(str, size, data_+i, size)) return i;
    return npos;
  }

  cstring cstring::left(uint size) const {
    cstring str(data_, size);
    return std::move(str);
  }
  cstring cstring::right(uint size) const {
    cstring str(data_+size_-size, size);
    return std::move(str);
  }
  cstring cstring::sub(uint pos, uint size) const {
    cstring str(data_+pos, size);
    return std::move(str);
  }

  array<cstring> cstring::split(cchar *str, uint size) const {
    array<cstring> arr;
    if (!str) return std::move(arr);
    if (size == csize) size = str_len(str);
    if (size > size_) return std::move(arr);
    uint bpos = 0;
    for (uint i = 0; i < size_ - size; ++i)
      if (str_equal(str, size, data_+i, size)) {
        cstring str(data_+bpos, i-bpos);
        arr.add(std::move(str));
        i += size;
        bpos = i;
      }
    return std::move(arr);
  }

  //------------------//
  string::string() : cstring(0, 0) {}
  string::string(cchar *src, uint size) : cstring(src, size) {}
  string::~string() {}
} // end namespace base